﻿using dnlib.DotNet;

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UnmanagedExportsTool
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length <= 0)
            {
                args = new string[] { @"E:\codes\dingzhi\YDPluginDev\YDPluginDev\YDTechUnpack\bin\Debug\EPluginTest.dll" };
            }
            Export(args[0]);
        }


        private static void Export(string dllName)
        {
            using (var fileStream = new FileStream(dllName, FileMode.Open, FileAccess.ReadWrite))
            using (var cache = new MemoryStream())
            {

                fileStream.CopyTo(cache);
                fileStream.Position = 0;

                var module = ModuleDefMD.Load(cache);

                Console.WriteLine("加载程序集 \"{0}\".", module.Assembly.FullName);

                var exports = from type in module.Types
                              from method in type.Methods
                              where method.IsStatic
                              let attr = method.CustomAttributes.Find("YDTech.ERobotLoader.DllExportAttribute")

                              where !(attr is null)
                              select new
                              {
                                  Method = method,
                                  Attribute = attr
                              };

                if (!exports.Any())
                {
                    Console.WriteLine("No methods were found.");
                    return;
                }

                Console.WriteLine("Found {0} methods, exporting...", exports.Count());

                foreach (var export in exports)
                {
                    var method = export.Method;
                    var attr = export.Attribute;
                    //var entryPoint = attr.GetProperty(nameof(DllExportAttribute.EntryPoint))?.Value?.ToString();
                    var entryPoint = attr.GetProperty("EntryPoint")?.Value?.ToString();
                    if (string.IsNullOrEmpty(entryPoint))
                    {
                        Console.WriteLine("Exported method \"{0}\".", method.FullName);
                        method.ExportInfo = new MethodExportInfo();
                    }
                    else
                    {
                        Console.WriteLine("Exported method \"{0}\" using custom entry point \"{1}\".", method.FullName, entryPoint);
                        method.ExportInfo = new MethodExportInfo(entryPoint);
                    }

                    method.MethodSig.RetType = new CModOptSig(
                        module.CorLibTypes.GetTypeRef(
                            "System.Runtime.CompilerServices",
                            "CallConvStdcall"
                        ),
                        method.MethodSig.RetType
                    );

                    method.CustomAttributes.Remove(attr);
                }

                module.IsILOnly = false;

                Console.WriteLine("Writing exported assembly...");
                module.Write(fileStream);
            }

        }
    }
}
